package com.hilinos.gb.gb28181.processor.request.impl;

import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sip.RequestEvent;
import javax.sip.message.Request;

import com.google.common.eventbus.EventBus;
import com.hilinos.gb.gb28181.processor.SipProcessorFactory;
import com.hilinos.gb.gb28181.processor.event.DeviceKeepLiveEvent;
import com.hilinos.gb.gb28181.processor.request.IRequestProcessor;
import com.hilinos.gb.gb28181.repository.IDeviceRepository;
import com.hilinos.gb.gb28181.repository.mysql.mapper.po.Device;
import com.hilinos.gb.gb28181.repository.mysql.mapper.po.DeviceChannel;
import com.hilinos.gb.gb28181.utils.XmlUtil;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class MessageRequestProcessor implements IRequestProcessor {
    @Autowired
    private SipProcessorFactory sipProcessorFactory;

    @Autowired
    private EventBus eventBus;

    @Autowired
    private IDeviceRepository storager;

    private static final String MESSAGE_KEEP_ALIVE = "Keepalive";
    private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload";
    private static final String MESSAGE_CATALOG = "Catalog";
    private static final String MESSAGE_DEVICE_INFO = "DeviceInfo";
    private static final String MESSAGE_ALARM = "Alarm";
    private static final String MESSAGE_RECORD_INFO = "RecordInfo";

    @SneakyThrows
    @Override
    public void process(RequestEvent requestEvent) {
        Element rootElement = getRootElement(requestEvent);
        String cmdType = XmlUtil.getText(rootElement, "CmdType");
        if (MESSAGE_KEEP_ALIVE.equals(cmdType)) {
            log.debug("接收到KeepAlive消息");
            processMessageKeepAlive(requestEvent);
        } else if (MESSAGE_CONFIG_DOWNLOAD.equals(cmdType)) {
            log.debug("接收到ConfigDownload消息");
        } else if (MESSAGE_CATALOG.equals(cmdType)) {
            log.debug("接收到Catalog消息");
            processMessageCatalogList(requestEvent);
        } else if (MESSAGE_DEVICE_INFO.equals(cmdType)) {
            log.debug("接收到DeviceInfo消息");
            processMessageDeviceInfo(requestEvent);
        } else if (MESSAGE_ALARM.equals(cmdType)) {
            log.debug("接收到Alarm消息");
            processMessageAlarm(requestEvent);
        } else if (MESSAGE_RECORD_INFO.equals(cmdType)) {
            log.debug("接收到RecordInfo消息");
            processMessageRecordInfo(requestEvent);
        }
    }

    /**
     * 收到deviceInfo设备信息请求 处理
     *
     * @param evt
     */
    private void processMessageDeviceInfo(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            Element deviceIdElement = rootElement.element("DeviceID");
            String deviceId = deviceIdElement.getText();

            Device device = storager.queryVideoDevice(deviceId);
            if (device == null) {
                return;
            }
            device.setDeviceName(XmlUtil.getText(rootElement, "DeviceName"));
            device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
            device.setModel(XmlUtil.getText(rootElement, "Model"));
            device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
            storager.update(device);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***
     * 收到catalog设备目录列表请求 处理
     * 
     * @param evt
     */
    @SneakyThrows
    private void processMessageCatalogList(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            Element deviceIdElement = rootElement.element("DeviceID");
            String deviceId = deviceIdElement.getText();
            Element deviceListElement = rootElement.element("DeviceList");
            if (deviceListElement == null) {
                return;
            }
            Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
            if (deviceListIterator != null) {
                Device device = storager.queryVideoDevice(deviceId);
                if (device == null) {
                    return;
                }
                Map<String, DeviceChannel> channelMap = device.getChannelMap();
                if (channelMap == null) {
                    channelMap = new HashMap<>(5);
                    device.setChannelMap(channelMap);
                }
                // 遍历DeviceList
                while (deviceListIterator.hasNext()) {
                    Element itemDevice = deviceListIterator.next();

                    Element channelDeviceElement = itemDevice.element("DeviceID");
                    if (channelDeviceElement == null) {
                        continue;
                    }
                    String channelDeviceId = channelDeviceElement.getText().toString();
                    Element channdelNameElement = itemDevice.element("Name");
                    String channelName = channdelNameElement != null ? channdelNameElement.getText().toString() : "";
                    Element statusElement = itemDevice.element("Status");
                    String status = statusElement != null ? statusElement.getText() : "ON";
                    DeviceChannel deviceChannel = channelMap.containsKey(channelDeviceId)
                            ? channelMap.get(channelDeviceId)
                            : new DeviceChannel();
                    deviceChannel.setChannelName(channelName);
                    deviceChannel.setChannelId(channelDeviceId);
                    if (status.equals("ON")) {
                        deviceChannel.setOnline("1");
                    }
                    if (status.equals("OFF")) {
                        deviceChannel.setOnline("0");
                    }

                    deviceChannel.setManufacturer(XmlUtil.getText(itemDevice, "Manufacturer"));
                    deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
                    deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
                    deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
                    deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
                    deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
                    deviceChannel.setParental(
                            itemDevice.element("Parental") == null ? "0" : XmlUtil.getText(itemDevice, "Parental"));
                    deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentId"));
                    deviceChannel.setSafetyWay(
                            itemDevice.element("SafetyWay") == null ? "0" : XmlUtil.getText(itemDevice, "SafetyWay"));
                    deviceChannel.setRegisterWay(itemDevice.element("RegisterWay") == null ? "1"
                            : XmlUtil.getText(itemDevice, "RegisterWay"));
                    deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
                    deviceChannel.setCertValid(itemDevice.element("Certifiable") == null ? "0"
                            : XmlUtil.getText(itemDevice, "Certifiable"));
                    deviceChannel.setCertErrcode(
                            itemDevice.element("ErrCode") == null ? "0" : XmlUtil.getText(itemDevice, "ErrCode"));
                    deviceChannel.setCertEndtime(XmlUtil.getText(itemDevice, "EndTime"));
                    deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
                    String ip = XmlUtil.getText(itemDevice, "Address");
                    if (!isAvailIp(ip)) {
                        continue;
                    }
                    deviceChannel.setIp(ip);
                    deviceChannel.setPort(itemDevice.element("Port") == null ? 0
                            : Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
                    deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
                    deviceChannel.setLongitude(itemDevice.element("Longitude") == null ? 0.00F
                            : Float.parseFloat(XmlUtil.getText(itemDevice, "Longitude")));
                    deviceChannel.setLatitude(itemDevice.element("Latitude") == null ? 0.00F
                            : Float.parseFloat(XmlUtil.getText(itemDevice, "Latitude")));
                    channelMap.put(channelDeviceId, deviceChannel);
                }
                // 更新
                storager.update(device);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /***
     * 收到alarm设备报警信息 处理
     * 
     * @param evt
     */
    private void processMessageAlarm(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            Element deviceIdElement = rootElement.element("DeviceID");
            String deviceId = deviceIdElement.getText().toString();

            Device device = storager.queryVideoDevice(deviceId);
            if (device == null) {
                return;
            }
            device.setDeviceName(XmlUtil.getText(rootElement, "DeviceName"));
            device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
            device.setModel(XmlUtil.getText(rootElement, "Model"));
            device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
            storager.update(device);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /***
     * 收到keepalive请求 处理
     * 
     * @param evt
     */
    private void processMessageKeepAlive(RequestEvent evt) {
        try {
            Element rootElement = getRootElement(evt);
            String deviceId = XmlUtil.getText(rootElement, "DeviceID");
            // to心跳处理
            eventBus.post(DeviceKeepLiveEvent.builder().deviceId(deviceId).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /***
     * 收到catalog设备目录列表请求 处理
     * TODO 过期时间暂时写死180秒，后续与DeferredResult超时时间保持一致
     * 
     * @param evt
     */
    private void processMessageRecordInfo(RequestEvent evt) {
    }

    private Element getRootElement(RequestEvent evt) throws DocumentException {
        Request request = evt.getRequest();
        SAXReader reader = new SAXReader();
        reader.setEncoding("gbk");
        Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
        return xml.getRootElement();
    }

    private static final Pattern IP_PATTERN = Pattern
            .compile("((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}");

    private boolean isAvailIp(String ip) {
        try {
            if (ip == null) {
                return false;
            }
            Matcher matcher = IP_PATTERN.matcher(ip);
            if (matcher.find()) {
                return !ip.startsWith("0.") && !ip.endsWith(".0");
            }
        } catch (Exception ignored) {
        }
        return false;
    }

}
